<?php
/**
 * Cameo Library
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 *
 * @category   Cameo
 * @package    Cameo_SMS
 * @subpackage Gateway
 * @copyright  Copyright (c) 2008 Sylvain Filteau (http://www.cidsphere.com)
 */

/**
 * Cameo_SMS_Message
 */
require_once('Cameo/SMS/Message.php');

/**
 * Abstracted class to perform action to an SMS Gateway.
 *
 * @todo describe options for this class
 *
 * @category   Cameo
 * @package    Cameo_SMS
 * @subpackage Gateway
 * @copyright  Copyright (c) 2008 Sylvain Filteau (http://www.cidsphere.com)
 */
abstract class Cameo_SMS_Gateway_Abstract {

	/**
	 * List of required features
	 * 
	 * This list will be sent to the gateway in order to ensure that the 
	 * message will be in the same shape you wanted to.
	 *
	 * @var array
	 */
	protected $_features_required = array();

	/**
	 * Flag asking for a delivery acknowledgement.
	 * 
	 * It's the role of the gateway service to deliver this acknowledgement.
	 *
	 * @var bool
	 */
	protected $_delivery_ack = false;

	/**
	 * The username to connect to the gateway
	 *
	 * @var string
	 */
	protected $_username;

	/**
	 * The password to connect to the gateway
	 *
	 * @var unknown_type
	 */
	protected $_password;

	/**
	 * Constants used to set values in the options array
	 */
	const OPT_FEATURES_REQUIRED = 'features_required';
	const OPT_DELIVERY_ACK		= 'delivery_ack';
	const OPT_USERNAME			= 'username';
	const OPT_PASSWORD			= 'password';

	/**
	 * Constructor.
	 * 
	 * Initialize the gateway with some default options.
	 *
	 * @param array $default_options
	 */
	public function __construct(array $default_options = array()) {
		foreach ($default_options as $k => $v) {
			switch ($k) {
				case self::OPT_FEATURES_REQUIRED:
					$this->_features_required = (array) $v;
					break;
				case self::OPT_DELIVERY_ACK:
					$this->_delivery_ack = (bool) $v;
					break;
				case self::OPT_USERNAME:
					$this->_username = $v;
					break;
				case self::OPT_PASSWORD:
					$this->_password = $v;
			}
		}
	}

	/**
	 * Create a new message with this gateway associated
	 *
	 * @param array $message_options
	 * @return Cameo_SMS_Message
	 */
	public function createMessage(array $message_options = array()) {
		return new Cameo_SMS_Message($message_options, $this);
	}

	/**
	 * Send a message to the gateway.
	 * 
	 * The array returned will contain the destination number of the message.
	 * Ex.:
	 * <code>
	 * array(
	 * 	'to' => '123456789
	 * );
	 * </code>
	 *
	 * @param Cameo_SMS_Message $message
	 * @param array $options
	 * @return array 
	 */
	abstract public function send(Cameo_SMS_Message $message, array $options = array());

	/**
	 * Send a message to a batch of destination.
	 *
	 * The array returned will contain the destination number of the message.
	 * Ex.:
	 * <code>
	 * array(
	 * 	array(
	 * 		'to' => '123456789'
	 * 	),
	 *  array(
	 * 		'to' => '234567891'
	 * 	),
	 * );
	 * </code>
	 * 
	 * @param Cameo_SMS_Message $message The message to be sent
	 * @param array $values The list of destination
	 * @param array $options The options to set to the gateway
	 * @return array The array of destination
	 * 
	 * @throws Cameo_SMS_Exception When the list of destination is empty
	 */
	public function batch(Cameo_SMS_Message $message, array $values, array $options = array()) {
		if (!count($values)) {
			throw new Cameo_SMS_Exception('Cannot send the messages, there is no destination in for the batch');
		}

		$ret = array();
		foreach ($values as $number) {
			$message->setTo($number);
			$this->validateMessage($message);
			$ret[] = $this->send($message, $options);
		}

		return $ret;
	}

	/**
	 * Accessor.
	 * 
	 * Set the username used to authenticate
	 *
	 * @param string $username
	 */
	public function setUsername($username) {
		$this->_username = $username;
	}

	/**
	 * Accessor.
	 * 
	 * Get the username used to authenticate.
	 *
	 * @return string
	 */
	public function getUsername() {
		return $this->_username;
	}

	/**
	 * Accessor.
	 * 
	 * Set the password used to authenticate
	 *
	 * @param string $password
	 */
	public function setPassword($password) {
		$this->_password = $password;
	}

	/**
	 * Accessor.
	 * 
	 * Get the password used to authenticate
	 *
	 * @return string
	 */
	public function getPassword() {
		return $this->_password;
	}

	/**
	 * Add required features to the list.
	 * 
	 * The list of valid features is defined in the gateway class documentation
	 * 
	 * @todo validate the feature with the gateway.
	 *
	 * @param mixed $feature
	 */
	public function addRequiredFeature($feature) {
		$this->_features_required[] = $feature;
	}

	/**
	 * Accessor.
	 * 
	 * Reset the list of required features the the list provided.
	 * 
	 * If $features is not an array, the item will be converted to an array.
	 *
	 * @param mixed $features
	 */
	public function setRequiredFeatures($features) {
		$this->_features_required = (array) $features;
	}

	/**
	 * Accessor.
	 * 
	 * Get the list of required features
	 *
	 * @return array
	 */
	public function getRequiredFeatures() {
		return $this->_features_required;
	}

	/**
	 * Accessor.
	 * 
	 * Set true if you want a delivery acknowledgement. The mechanism for 
	 * sending this acknowledgement is up to the gateway.
	 *
	 * @param bool $ack
	 */
	public function setDeliveryAcknowledgement($ack) {
		$this->_delivery_ack = $ack;
	}

	/**
	 * Accessor.
	 * 
	 * Get if a delivery acknowledgement is requested.
	 *
	 * @return bool
	 */
	public function deliveryAcknowledgement() {
		return $this->_delivery_ack;
	}

	/**
	 * Get the options as an array to be used by the gateway.
	 * 
	 * If options are specified in parameters, it will automatically override
	 * those specified previously in th object.
	 *
	 * @param array $new_options overrides options specified previously in the object.
	 * @return array All the options.
	 */
	protected function _getOptions(array $new_options = array()) {
		$options = array();

		if (count($this->_features_required)) {
			$options[self::OPT_FEATURES_REQUIRED] = $this->_features_required;
		}

		if ($this->_delivery_ack) {
			$options[self::OPT_DELIVERY_ACK] = $this->_delivery_ack;
		}

		return array_merge($options, $new_options);
	}

	/**
	 * Validate a message.
	 * 
	 * @todo reconsider the validation in the gateway. I think some validations should be done in the message itself.
	 *
	 * @param Cameo_SMS_Message $message
	 * @return bool
	 * 
	 * @throws Cameo_SMS_Exception When the text is empty
	 * @throws Cameo_SMS_Exception When the destination is empty
	 * @throws Cameo_SMS_Exception When the destination is not a numeric
	 * @throws Cameo_SMS_Exception When the source is empty
	 */
	public function validateMessage(Cameo_SMS_Message $message) {
		if ($message->getText() == '') {
			throw new Cameo_SMS_Exception('Cannot send the message, there is no text in the message body');
		}

		if ($message->getTo() == '') {
			throw new Cameo_SMS_Exception('Cannot send the message, there is no destination in the message');
		}

		if (!is_numeric($message->getTo())) {
			throw new Cameo_SMS_Exception('Cannot send the message, the destination provided is not numeric');
		}

		if ($message->getFrom() == '') {
			throw new Cameo_SMS_Exception('Cannot send the message, there is no source in the message');
		}

		return true;
	}

	//public abstract function test();

}